//////////////////////////////////////////////////////////////////////
//
// OGL32TEX.C
//  by Blaine Hodge
//

#include <windows.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <stdio.h>



// Declarations //////////////////////////////////////////////////////

GLuint LoadTextureRAW( const char * filename, int wrap );
void FreeTexture( GLuint texture );

LRESULT CALLBACK WndProc( HWND hWnd, UINT message,
	WPARAM wParam, LPARAM lParam );

VOID EnableOpenGL( HWND hWnd, HDC * hDC, HGLRC * hRC );
VOID DisableOpenGL( HWND hWnd, HDC hDC, HGLRC hRC );



// WinMain ///////////////////////////////////////////////////////////

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
	LPSTR lpCmdLine, int iCmdShow )
{
	WNDCLASS wc;
	HWND hWnd;
	HDC hDC;
	HGLRC hRC;
	MSG msg;
	BOOL bQuit = FALSE;

	GLuint texture;
	float theta = 0.0f;

	// register window class
	wc.style = CS_OWNDC;
	wc.lpfnWndProc = WndProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = hInstance;
	wc.hIcon = LoadIcon( NULL, IDI_APPLICATION );
	wc.hCursor = LoadCursor( NULL, IDC_ARROW );
	wc.hbrBackground = (HBRUSH)GetStockObject( BLACK_BRUSH );
	wc.lpszMenuName = NULL;
	wc.lpszClassName = TEXT("GLSample");
	RegisterClass( &wc );

	// create main window
	hWnd = CreateWindow( 
		TEXT("GLSample"), TEXT("OpenGL Texture Sample"), 
		WS_CAPTION | WS_POPUPWINDOW | WS_VISIBLE,
		0, 0, 256, 256,
		NULL, NULL, hInstance, NULL );

	// enable OpenGL for the window
	EnableOpenGL( hWnd, &hDC, &hRC );

	// load our texture
	texture = LoadTextureRAW( "texture.raw", TRUE );

	// program main loop
	while ( !bQuit ) {

		// check for messages
		if ( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) {

			// handle or dispatch messages
			if ( msg.message == WM_QUIT ) {
				bQuit = TRUE;
			} else {
				TranslateMessage( &msg );
				DispatchMessage( &msg );
			}

		} else {

			// OpenGL animation code goes here

			glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
			glClear( GL_COLOR_BUFFER_BIT );

			// setup texture mapping
			glEnable( GL_TEXTURE_2D );
			glBindTexture( GL_TEXTURE_2D, texture );

			glPushMatrix();
			glRotatef( theta, 0.0f, 0.0f, 1.0f );
			glBegin( GL_QUADS );
			glTexCoord2d(0.0,0.0); glVertex2d(-1.0,-1.0);
			glTexCoord2d(1.0,0.0); glVertex2d(+1.0,-1.0);
			glTexCoord2d(1.0,1.0); glVertex2d(+1.0,+1.0);
			glTexCoord2d(0.0,1.0); glVertex2d(-1.0,+1.0);
			glEnd();
			glPopMatrix();

			SwapBuffers( hDC );

			theta += 1.0f;

		}

	}

	// free the texture
	FreeTexture( texture );

	// shutdown OpenGL
	DisableOpenGL( hWnd, hDC, hRC );

	// destroy the window explicitly
	DestroyWindow( hWnd );

	return msg.wParam;

}



// Texture ///////////////////////////////////////////////////////////

// load a 256x256 RGB .RAW file as a texture
GLuint LoadTextureRAW( const char * filename, int wrap )
{
	GLuint texture;
	int width, height;
	BYTE * data;
	FILE * file;

	// open texture data
	file = fopen( filename, "rb" );
	if ( file == NULL ) return 0;

	// allocate buffer
	width = 256;
	height = 256;
	data = (BYTE *)malloc( width * height * 3 );

	// read texture data
	fread( data, width * height * 3, 1, file );
	fclose( file );

	// allocate a texture name
	glGenTextures( 1, &texture );

	// select our current texture
	glBindTexture( GL_TEXTURE_2D, texture );

	// select modulate to mix texture with color for shading
	glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );

	// when texture area is small, bilinear filter the closest MIP map
	glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
		GL_LINEAR_MIPMAP_NEAREST );
	// when texture area is large, bilinear filter the first MIP map
	glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );

	// if wrap is true, the texture wraps over at the edges (repeat)
	//       ... false, the texture ends at the edges (clamp)
	glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
		wrap ? GL_REPEAT : GL_CLAMP );
	glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
		wrap ? GL_REPEAT : GL_CLAMP );

	// build our texture MIP maps
	gluBuild2DMipmaps( GL_TEXTURE_2D, 3, width,
		height, GL_RGB, GL_UNSIGNED_BYTE, data );

	// free buffer
	free( data );

	return texture;

}

void FreeTexture( GLuint texture )
{

	glDeleteTextures( 1, &texture );

}



// Window Proc ///////////////////////////////////////////////////////

LRESULT CALLBACK WndProc( HWND hWnd, UINT message,
	WPARAM wParam, LPARAM lParam )
{

	switch ( message ) {

	case WM_CREATE:
		return 0;

	case WM_CLOSE:
		PostQuitMessage( 0 );
		return 0;

	case WM_DESTROY:
		return 0;

	case WM_KEYDOWN:
		switch ( wParam ) {

		case VK_ESCAPE:
			PostQuitMessage( 0 );
			return 0;

		}
		return 0;

	default:
		return DefWindowProc( hWnd, 
			message, wParam, lParam );

	}

}


// OpenGL ////////////////////////////////////////////////////////////

// Enable OpenGL

VOID EnableOpenGL( HWND hWnd, HDC * hDC, HGLRC * hRC )
{
	PIXELFORMATDESCRIPTOR pfd;
	int iFormat;

	// get the device context (DC)
	*hDC = GetDC( hWnd );

	// set the pixel format for the DC
	ZeroMemory( &pfd, sizeof( pfd ) );
	pfd.nSize = sizeof( pfd );
	pfd.nVersion = 1;
	pfd.dwFlags = PFD_DRAW_TO_WINDOW | 
		PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
	pfd.iPixelType = PFD_TYPE_RGBA;
	pfd.cColorBits = 24;
	pfd.cDepthBits = 16;
	pfd.iLayerType = PFD_MAIN_PLANE;
	iFormat = ChoosePixelFormat( *hDC, &pfd );
	SetPixelFormat( *hDC, iFormat, &pfd );

	// create and enable the render context (RC)
	*hRC = wglCreateContext( *hDC );
	wglMakeCurrent( *hDC, *hRC );

}

// Disable OpenGL

VOID DisableOpenGL( HWND hWnd, HDC hDC, HGLRC hRC )
{
	wglMakeCurrent( NULL, NULL );
	wglDeleteContext( hRC );
	ReleaseDC( hWnd, hDC );
}
